/*
* @Author: z.p
* @Project: Zdotp
* @Date: 2024年12月31日
* @Description: 后端接口
*/

using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

namespace Zdotp.Net.Kcp.App;

public sealed class ServerApp : ZyKcpApp
{
    private Dictionary<uint, KcpSession> _sessions;
    private static readonly Lock Lock = new();
    
    public bool HasSession(uint sessionId) => _sessions.ContainsKey(sessionId);
    
    public override void Start(string ip, int port) 
    {
        base.Start(ip, port);
        _sessions = new Dictionary<uint, KcpSession>();

        Udp = new UdpClient(new IPEndPoint(IPAddress.Parse(ip), port));
        if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
            Udp.Client.IOControl((IOControlCode)(-1744830452), "\0\0\0\0"u8.ToArray(), null);
        }
        
        RemotePoint = new IPEndPoint(IPAddress.Parse(ip), port);
        
        Task.Run(ServerReceive, Cts.Token);
    }

    public void SendMsgToAllClient(byte[] msg)
    {
        foreach (var item in _sessions)
        {
            SendMsgToClient(item.Value, msg);
        }
    }

    public bool SendMsgToClient(uint sessionId, byte[] msg)
    {
        // 如果存在就发送
        if (_sessions.TryGetValue(sessionId, out var session))
        {
            SendMsgToClient(session, msg);
            return true;
        }
        return false;
    }

    private void SendMsgToClient(KcpSession session, byte[] msg) => session.SendMessage(msg);
    
    /// <summary>
    /// 静态id, 就能实现多个gate网关不冲突存储
    /// </summary>
    private static uint _maxSessionId;

    private uint GetNewId() 
    {
        lock(Lock) 
        {
            while(true) 
            {
                ++_maxSessionId;
                if(_maxSessionId == uint.MaxValue) _maxSessionId = 1;
                if(!_sessions.ContainsKey(_maxSessionId)) break;
            }
        }
        return _maxSessionId;
    }
    
    private async void ServerReceive()
    {
        try
        {
            while(true) 
            {
                var result = await Udp.ReceiveAsync(Cts.Token);
                var id = BitConverter.ToUInt32(result.Buffer, 0);
                if(id == 0 && result.Buffer.Length == 4)
                {
                    id = GetNewId();
                    var session = new KcpSession(this, id, SendUdpMessage, result.RemoteEndPoint);
                    session.SessionStart();
                    lock (Lock) { _sessions.Add(id, session); }
                    var idBytes = BitConverter.GetBytes(id);
                    SendUdpMessage(idBytes, result.RemoteEndPoint);
                }
                else 
                {
                    if(_sessions.TryGetValue(id, out var session)) 
                    {
                        session.ReceiveData(result.Buffer);
                    }
                }
            }
        }
        catch (Exception e)
        {
            KcpLogger.Error($"ServerReceive Task error. {e}");
        }
    }

    public void ServerCloseClient(uint sid)
    {
        if (_sessions.TryGetValue(sid, out var session))
        {
            session?.CloseSession();
            lock(Lock) _sessions.Remove(sid);
        }
        else KcpLogger.Error($"Session:{sid} cannot find");
    }
    
    public override void Close()
    {
        foreach(var item in _sessions) item.Value?.CloseSession();
        Udp?.Close();
        Cts.Cancel();
    }
}